package com.lz.yz.reward.service.impl;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.lz.yz.base.common.ValidatorUtil;
import com.lz.yz.base.entity.ReturnValue;
import com.lz.yz.base.util.Constant;
import com.lz.yz.base.util.DateHelper;
import com.lz.yz.base.util.DateUtil;
import com.lz.yz.base.util.StringUtil;
import com.lz.yz.core.AbstractService;
import com.lz.yz.member.service.PropertyInfoService;
import com.lz.yz.order.enumeration.TurnoversType;
import com.lz.yz.order.service.TurnoversService;
import com.lz.yz.outing.config.InitConfig;
import com.lz.yz.outing.config.WechatConfig;
import com.lz.yz.outing.service.CreateNo;
import com.lz.yz.outing.service.SyncLockService;
import com.lz.yz.outing.util.UploadOSSFile;
import com.lz.yz.reward.bean.RewardBean;
import com.lz.yz.reward.dao.CrowdfundingShareMapper;
import com.lz.yz.reward.dao.RewardMapper;
import com.lz.yz.reward.entity.CrowdfundingShare;
import com.lz.yz.reward.model.CrowdfundingShareModel;
import com.lz.yz.reward.param.RewardParam;
import com.lz.yz.reward.service.CrowdfundingShareService;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.math.BigDecimal;
import java.util.*;
import java.util.List;
import java.util.concurrent.TimeUnit;


/**
 * Created by CodeGenerator on 2017/11/24.
 */
@Service
@Transactional
public class CrowdfundingShareServiceImpl extends AbstractService<CrowdfundingShare> implements CrowdfundingShareService {

    @Autowired
    CrowdfundingShareMapper crowdfundingShareMapper;

    @Autowired
    SyncLockService syncLockService;

    @Autowired
    RewardMapper rewardMapper;

    @Autowired
    CreateNo createNo;

    @Autowired
    PropertyInfoService propertyInfoService;

    @Autowired
    TurnoversService turnoversService;

    @Autowired
    InitConfig initConfig;

    @Resource(name = "redisTemplate")
    RedisTemplate<String, Object> redisTemplate;

    @Autowired
    UploadOSSFile uploadOSSFile;

    /**
     * 修改分享状态
     */
    @Override
    public ReturnValue updateCrowdfundingShare(CrowdfundingShareModel model) {
        CrowdfundingShare share = this.findBy("shareNo", model.getShareNo());
        if (share == null) {
            return ReturnValue.generateFalseReturnValue("分享码无效!");
        }
        int to = model.getTo();
        int from = share.getFrom();
        if (share.getFrom().equals(to)) {
            return ReturnValue.generateTrueMsgReturnValue("自己点击了自己的分享!");
        }



        CrowdfundingShare insert = new CrowdfundingShare();
        insert.setRewardId(share.getRewardId());
        insert.setFrom(share.getFrom());
        insert.setTo(to);

        //被分享人第一次进来
        if (crowdfundingShareMapper.selectOne(insert) == null) {
            insert.setShareDate(DateHelper.puaseDateYMD(DateHelper.formatYMD(new Date())));
            crowdfundingShareMapper.insertSelective(insert);
        }

        //增加积分
        ValueOperations<String, Object> ops = redisTemplate.opsForValue();
        String cacheKey = this.getCacheRewardShareKey(from);
        //当天分享次数少于5增加积分
        if(ops.get(cacheKey)==null){
            ops.set(cacheKey,0,1,TimeUnit.DAYS);
        }

        if(Integer.valueOf(ops.get(cacheKey).toString())<5){

            CrowdfundingShare select = new CrowdfundingShare();
            select.setRewardId(share.getRewardId());
            select.setFrom(share.getFrom());
            select.setTo(to);
            if(crowdfundingShareMapper.select(select).size()!=0){ //查询from to 以及悬赏id确定唯一
                CrowdfundingShare crowdfundingShare = crowdfundingShareMapper.select(select).get(0);
                if(crowdfundingShare.getIsAdd().equals(0)){ //未加过积分增加积分后改变状态
                    propertyInfoService.increaseIntegral(from, new BigDecimal(10));
                    turnoversService.insert(from, TurnoversType.INTEGRAL, Constant.ADDITION, new BigDecimal(10), null);
                    ops.increment(cacheKey,1);

                    CrowdfundingShare update = new CrowdfundingShare();
                    update.setId(crowdfundingShare.getId());
                    update.setIsAdd(1);
                    crowdfundingShareMapper.updateByPrimaryKeySelective(update);
                }
            }
        }
        return ReturnValue.generateTrueMsgReturnValue("更新分享状态成功");
    }


    public String getCacheRewardShareKey(Integer accountId){
        return Constant.REWARD_SHARE_KEY + "_"+ DateUtil.format(new Date())  + "_" + accountId;
    }

    /**
     * 生成分享卡
     */
    @Override
    public ReturnValue createShareCard(RewardParam param) {
        //控制双重点击事件
        if (!syncLockService.syncLock("createShareCard/" + param.getRewardId(), 1, TimeUnit.SECONDS)) {
            return ReturnValue.generateFalseReturnValue("请不要重复提交!");
        }
        RewardBean rewardBean = rewardMapper.queryRewardSharePage(param);
        if (rewardBean == null) {
            return ReturnValue.generateFalseReturnValue("分享的悬赏id无效!");
        }
        //分享后记录用户操作
        CrowdfundingShare select = new CrowdfundingShare(param.getLoginId(), param.getRewardId());
        select.setShareNo(null);
        int size = crowdfundingShareMapper.select(select).size();
        if (size == 0) {
            select.setShareDate(DateHelper.puaseDateYMD(DateHelper.formatYMD(new Date())));
            select.setShareNo(createNo.getNo(param.getLoginId(), ""));
            crowdfundingShareMapper.insertSelective(select);
        } else {
            select = crowdfundingShareMapper.select(select).get(0);

            //更新分享时间来统一每日加积分上限需求
            CrowdfundingShare update = new CrowdfundingShare();
            update.setId(select.getId());
            update.setShareDate(DateHelper.puaseDateYMD(DateHelper.formatYMD(new Date())));
            crowdfundingShareMapper.updateByPrimaryKeySelective(update);
        }
        String shareNo = select.getShareNo();

        //拼接配置网页授权url
        String url1 = StringUtil.replaceAccessTokenReg(WechatConfig.SQHD_URL, "appid", initConfig.getWechatAppid());
        String url2 = StringUtil.replaceAccessTokenReg(url1, "redirect_uri", param.getShareUrl());
        String url3 = StringUtil.replaceAccessTokenReg(url2, "state", shareNo);
        return ReturnValue.generateTrueReturnValue(url3, this.changeImage(rewardBean, url3));
    }

    /**
     * 图片合成后上传
     *
     * @return
     */
    public String changeImage(RewardBean rewardShareBean, String shareUrl) {
        String returnFileName = null;
        try {

            File picFile = File.createTempFile("temp", ".jpg");//创建临时文件
            FileOutputStream fos = new FileOutputStream(picFile);
            FileInputStream fis = new FileInputStream(picFile);
            try {
                //生成二维码
                int width = 40;
                int height = 40;
                ByteArrayOutputStream os = new ByteArrayOutputStream();//新建流。
                String format = "png";
                Hashtable hints = new Hashtable();
                hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
                BitMatrix bitMatrix = new MultiFormatWriter().encode(shareUrl, BarcodeFormat.QR_CODE, width, height, hints);
                BufferedImage wmImage = MatrixToImageWriter.toBufferedImage(bitMatrix);
                ImageIO.write(wmImage, format, os);//利用ImageIO类提供的write方法，将bi以png图片的数据模式写入流。

                //这里处理业务逻辑
                InputStream imagein2 = new ByteArrayInputStream(os.toByteArray());


                InputStream backimage = Thread.currentThread().getContextClassLoader().getResourceAsStream("share.jpg");
                BufferedImage image = ImageIO.read(backimage);
                BufferedImage image2 = ImageIO.read(imagein2);
                Graphics g = image.getGraphics();
                g.drawImage(image2, 500, 1300, 160, 160, null);

                JPEGImageEncoder enc = JPEGCodec.createJPEGEncoder(fos);
                enc.encode(image);

                BufferedImage bimg = ImageIO.read(fis);
                //内容
                if (rewardShareBean.getContent() != null) {
                    //得到Graphics2D 对象
                    Graphics2D g2d = (Graphics2D) bimg.getGraphics();
                    //设置颜色和画笔粗细
                    g2d.setColor(Color.black);
                    g2d.setStroke(new BasicStroke(13));
                    g2d.setFont(new Font("宋体", Font.PLAIN, 20));
                    String content =rewardShareBean.getContent().replaceAll(ValidatorUtil.REGEX_HTML, "");
                    if(content.length()>250){
                        content = content.substring(0,250)+"...";
                    }
                    this.drawStringMultiLine(g2d,content,580,120,90, 610,new Font("宋体", Font.PLAIN, 20));
                }

                //分享数
                String shareNum = "0";
                if (rewardShareBean.getShareCount() != null) {
                    shareNum = rewardShareBean.getShareCount().toString();
                }
                Graphics2D g3d = (Graphics2D) bimg.getGraphics();
                //设置颜色和画笔粗细
                g3d.setColor(Color.CYAN);
                // g2d.setStroke(new BasicStroke(5));
                g3d.setStroke(new BasicStroke(13));
                g3d.setFont(new Font("宋体", Font.PLAIN, 40));
                g3d.drawString(shareNum, 230, 350);

                //职位和姓名
                if (rewardShareBean.getPosition() != null || rewardShareBean.getRealName() != null) {
                    Graphics2D g4d = (Graphics2D) bimg.getGraphics();
                    //设置颜色和画笔粗细
                    g4d.setColor(Color.CYAN);
                    g4d.setStroke(new BasicStroke(13));
                    Font font = new Font("宋体", Font.PLAIN, 35);
                    g4d.setFont(font);
                    String str = rewardShareBean.getPosition()==null?"":rewardShareBean.getPosition();
                    String temp = rewardShareBean.getRealName()==null?str+"":str + "-" +rewardShareBean.getRealName();

                    //居中显示
                    // 计算文字长度，计算居中的x点坐标
                    FontMetrics fm = g4d.getFontMetrics(font);
                    int textWidth = fm.stringWidth(temp);
                    int widthX = (image.getWidth() - textWidth) / 2;
                    // 表示这段文字在图片上的位置(x,y) .第一个是你设置的内容。
                    g4d.drawString(temp,widthX,480);
                }

                //公司名
                if (rewardShareBean.getCompanyName() != null) {
                    Graphics2D g5d = (Graphics2D) bimg.getGraphics();
                    //设置颜色和画笔粗细
                    g5d.setColor(Color.CYAN);
                    g5d.setStroke(new BasicStroke(13));
                    Font font = new Font("宋体", Font.PLAIN, 35);
                    g5d.setFont(font);

                    String companyName = rewardShareBean.getCompanyName();
                    //居中显示
                    // 计算文字长度，计算居中的x点坐标
                    FontMetrics fm = g5d.getFontMetrics(font);
                    int textWidth = fm.stringWidth(companyName);
                    int widthX = (image.getWidth() - textWidth) / 2;
                    // 表示这段文字在图片上的位置(x,y) .第一个是你设置的内容。
                    g5d.drawString(companyName,widthX,420);
                }
                //文件流上传
                ByteArrayOutputStream bytes = new ByteArrayOutputStream();
                ImageIO.write(bimg, "JPG", bytes);
                ByteArrayInputStream swapStream = new ByteArrayInputStream(bytes.toByteArray());
                returnFileName = uploadOSSFile.uploadFile(swapStream, "temp.jpg");

                backimage.close();
                imagein2.close();
            } finally {
                //关闭临时文件
                fos.flush();
                fos.close();
                picFile.deleteOnExit();//程序退出时删除临时文件
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return returnFileName;
    }


    /**
     * 描述: 长字符串缩小字体自动换行
     *
     * @param g
     * @param text       字符串
     * @param lineWidth  单元格宽度
     * @param cellHeight 单元格高度
     * @param x          x坐标
     * @param y          y坐标
     * @param cellFont   原字体
     */

    public void drawStringMultiLine(Graphics2D g, String text, int lineWidth, int cellHeight, int x, int y, Font cellFont) {
        FontMetrics m = g.getFontMetrics();
        if (m.stringWidth(text) < lineWidth) {
            g.drawString(text, x, y);
        } else {
            /* 使用当前字体, 根据单元格宽度计算出应该打印行数 */
            int strWidth = 0;
            int widthLine = 1;
            char[] chars = text.toCharArray();
            for (int i = 0; i < chars.length; i++) {
                if (m.charWidth(chars[i]) > lineWidth) { //单个字比单元格宽，肯定缩小字体
                    widthLine = 10000;
                    break;
                }

                strWidth += m.charWidth(chars[i]);
                if (strWidth > lineWidth) {
                    widthLine++;
                    strWidth = 0;
                    i--;
                }
            }

            String name = "Serif";
            int style = Font.PLAIN;
            int high = 16; //默认16号字
            Font font = null;
            if (cellFont != null) {
                name = cellFont.getName();
                style = cellFont.getStyle();
                high = cellFont.getSize();
            }
            int fontHeight = m.getAscent() + m.getDescent();

            /* 计算能打出全部内容时的最大字体 */
            int heightLine = 2;//一个单元格只能写2行
            while (widthLine > heightLine) {
            /* 缩小字体，重复计算应该打印行数和允许打印行数 */
                font = new Font(name, style, --high);

                m = g.getFontMetrics(font);

            /* 字体高度 */
                fontHeight = m.getAscent() + m.getDescent();
                if (fontHeight <= 0)
                    return;

                strWidth = 0;
/* 使用当前字体, 根据单元格宽度计算出应该打印行数 */
                widthLine = 1;
                for (int i = 0; i < chars.length; i++) {
                    if (m.charWidth(chars[i]) > lineWidth) { //单个字比单元格宽，肯定缩小字体
                        widthLine = 10000;
                        break;
                    }
                    strWidth += m.charWidth(chars[i]);
                    if (strWidth > lineWidth) {
                        widthLine++;
                        strWidth = 0;
                        i--;
                    }
                }

                /* 使用当前字体时,根据单元格高度计算出允许打印行数 */
                heightLine = 0;
                while ((fontHeight * heightLine) <= cellHeight)//最后一行没有行间距
                    heightLine++;
                heightLine--;

                if (widthLine <= heightLine)
                    break;
            }

            Font oldFont = g.getFont();
            Stroke oldStroke = g.getStroke();
            g.setFont(font);
            g.setStroke(new BasicStroke(1.0f));

            /* 分行，计算各行文字内容 */
            List<String> rows=new ArrayList<>();
            int fromIndex = 0;
            strWidth = 0;
            for (int bgn = 0; bgn < text.length(); ) {//逐个字符累加计算长度,超过行宽,自动换行
                strWidth += m.charWidth(chars[bgn]);

                if (strWidth > lineWidth) {
                    rows.add(text.substring(fromIndex, bgn));

                    strWidth = 0;
                    fromIndex = bgn;
                } else
                    bgn++;
            }

            if (fromIndex < text.length()) // 加上最后一行
                rows.add(text.substring(fromIndex, text.length()));
            String element;
            for (Iterator iter = rows.iterator(); iter.hasNext(); ) {
                element = (String) iter.next();
                /* 绘制字符串 */
                g.drawString(element, (float) x, (float) (y + m.getAscent()));
                y += fontHeight;
            }
            g.setFont(oldFont);
            g.setStroke(oldStroke);
        }
    }


}
